package controller.commands;

import controller.Constants;
import controller.StoreFactory;
import core.DispatcherPort;
import core.InPort;
import core.Node;
import core.OutPort;
import core.Port;
import core.TriggerPort;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import load.storage.StoreIO;
import load.parent.Store;
import load.util.Comment;
import load.ogp.Reference;
import load.parent.Wrapper;
import model.graphic.GraphicModel;
import model.graphic.locations.Location;
import model.graphic.objects.GCommentNode;
import model.graphic.objects.GResizeHandle;
import model.graphic.objects.GNode;
import model.graphic.objects.GOutput;
import model.graphic.objects.GOutputPort;
import model.graphic.objects.GStoreNode;
import load.parent.Function;
import view.GUI.LabelReader;
import view.GUI.sidepanels.CustomArgument;

/**
 *
 * @author Administrator, modified by Ben
 */
public class AddNodeCommand extends BasicCommand {

    private Location location = null;
    private String type;
    private GNode gn;
    private Node n = null;
    private Class<?> c = null;
    private ModusType modus;
    private GOutput gOutput;
    static Map<String, Integer> indices = new HashMap<String, Integer>();
    private enum ModusType {
        Store,
        Normal,
        Comment,
        Reference,
        Function,
        Class
    };

    public AddNodeCommand(GraphicModel gmodel, GOutput gOutput, Class<?> c){
        this.type = c.getSimpleName();
        this.c = c;
        this.gOutput = gOutput;
        if(c.equals(Function.class)){
            modus = ModusType.Function;
        }else   modus = ModusType.Reference;
    }

    public AddNodeCommand(GraphicModel gmodel, Class<?> c) {
        this.type = c.getSimpleName();
        this.c = c;
        if(Store.class.isAssignableFrom(c)){
            modus = ModusType.Store;
        }else if(Comment.class.isAssignableFrom(c)){
            modus = ModusType.Comment;
        }else if(load.ogp.Class.class.isAssignableFrom(c)){
            modus = ModusType.Class;
        }else if(load.ogp.Reference.class.isAssignableFrom(c)){
            modus = ModusType.Reference;
        }else{
            modus = ModusType.Normal;
        }
    }

    public GNode getGNode() {
        return gn;
    }

    @Override
    public void execute() {
        if (modus == ModusType.Normal) {
            Object cbo = null;
            try {
                cbo = c.newInstance();
            } catch (Exception ex) {
                //deny
            }

            n = (Node) cbo;
            boolean b = false;
            for (Field f : c.getDeclaredFields()) {
                //System.out.println("F" + f.getName());
                if (f.getName().equals("_value")) {
                    b = true;
                }
            }
            if (b) {
                gn = new GNode("NoName");
            } else {
                gn = new GNode();
            }
        } else if (modus == ModusType.Store) {//create a store
            n = StoreFactory.createStore(c.getSimpleName());
            if(!StoreIO.class.isAssignableFrom(c)){
                gn = new GStoreNode(((Store)n).getValue().toString(),gmodel);
            }else{
                gn = new GNode(((Store)n).getValue().toString());
            }
        }else if(modus == ModusType.Comment){
            n = new Comment();
            gn = new GCommentNode();
        }else if(modus == ModusType.Reference){
            gn = new GNode();
            String simpleName = null;
            if(gOutput != null){
                Class valClass = AddAttachmentCommand.getClassFromPort((GOutputPort)gOutput.getBase(), sync);
                simpleName = valClass.getSimpleName();
                n = new Reference(valClass);
            }else{
                try{
                    n = (Node)c.newInstance();
                    simpleName = ((load.ogp.Reference)n).getTargetClassName();
                }catch(Exception ex){
                }
            }
            type = "Reference<" +simpleName +">";
        }else if(modus == ModusType.Function){
            gn = new GNode();
            GNode g3 = (GNode)((GOutputPort)gOutput.getBase()).getBase();
            OutPort p = (OutPort)sync.get(g3).getPort(gOutput.getBase().getName());
            n = new Function((Function)p.getValue());
        }else if(modus == ModusType.Class){
            gn = new GNode();
            try{
                n = (Node)c.newInstance();
                type = "Class<" + ((load.ogp.Class)n).getTargetClassName() + ">";
            }catch(Exception ex){
            }
        }

        //set the name of the graphical node
        Integer nr = indices.get(n.getClass().getSimpleName());
        if(nr == null){
            nr = 1;
        }
        indices.put(n.getClass().getSimpleName(), nr + 1);
        String className = n.getClass().getSimpleName();
        if(c.isAssignableFrom(Wrapper.class)){
            className = ((Wrapper)n).getTargetClassName();
        }
        gn.setName(className + nr);
        //name is set
        ArrayList<BasicCommand> commands = new ArrayList<BasicCommand>();
        for (String pname : n.getPortMap().keySet()) {
            Port po = n.getPortMap().get(pname);
            if (TriggerPort.class.isAssignableFrom(po.getClass())) {
                //System.out.println("REFLECT TRIGGER");
                commands.add(new AddAttachmentCommand(Constants.getTriggerportString(), pname));
            } else if (InPort.class.isAssignableFrom(po.getClass())) {
                //System.out.println("REFLECT INPORT");
                commands.add(new AddAttachmentCommand(Constants.getInputportString(), pname));
            } else if (OutPort.class.isAssignableFrom(po.getClass())) {
                //System.out.println("REFLECT OUTPORT");
                commands.add(new AddAttachmentCommand(Constants.getOutputportString(), pname));
            } else if (DispatcherPort.class.isAssignableFrom(po.getClass())) {
                //System.out.println("REFLECT DISPATCHER");
                commands.add(new AddAttachmentCommand(Constants.getDispatcherportString(), pname));
            }
        }

        gn.setLocations(location);
        gn.setMinSize(40);
        gn.setType(type);

        gmodel.addObject(gn);
        gview.add(gn);

        if(modus != ModusType.Store || StoreIO.class.isAssignableFrom(c)){
            GResizeHandle rh = new GResizeHandle(gn, 50);
            gn.setResizer(rh);
            gmodel.addObject(rh);
            gview.add(rh);
        }

        cmodel.addObject(n);
        sync.put(n, gn);

        controller.getDebug().append(LabelReader.getInstance().getString("ANodeAddedToPanel"));

        commands.add(new RedrawCommand(gn));
        commands.add(new AddDefaultValuesCommand(gmodel,gn,n,gview));

        gview.notifyObservers(new MacroCommand(commands));
        gview.notifyObservers(new ClearSelectionCommand());
        gview.notifyObservers(new CustomArgument("tabRight", 1));

        if(modus == ModusType.Store){
            gn.setValue(gn.getValue());
        }
    }

    /**
     * @param location the location to set
     */
    public void setLocation(Location location) {
        this.location = location;
    }
}